package org.aceor.mddal.client.jdbc;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.aceor.mddal.client.jdbc.adapter.AbstractConnectionAdapter;
import org.aceor.mddal.common.utils.ExceptionUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by lxue on 16/4/5.
 */
public class ConnectionFacade extends AbstractConnectionAdapter implements Connection, ConnectionManager {

    private static final Logger log = LoggerFactory.getLogger(ConnectionFacade.class);

    private final DataSourceFacade dataSourceFacade;

    private final String username;
    private final String password;

    private boolean closed = false;
    private boolean isAutoCommit = true;

    private Set<Statement> openedStatements = Sets.newHashSet();

    private Map<String, Connection> connectionMap = Maps.newHashMap();

    private static final int maxTransactionDSCount = 1;

    public ConnectionFacade(DataSourceFacade dataSourceFacade, String username, String password) {
        this.dataSourceFacade = dataSourceFacade;
        this.username = username;
        this.password = password;
    }

    @Override
    public Connection getConnection(String dbIndex) throws SQLException {
        if (connectionMap.containsKey(dbIndex)) {
            return connectionMap.get(dbIndex);
        }
        Connection connection = getConnectionFromDs(dbIndex);
        if (null == connection) {
            throw new RuntimeException("无法通过groupDsKey"+dbIndex+"获取连接");
        }
        connection.setAutoCommit(isAutoCommit);
        if (!isAutoCommit) {
            //事务中,进行数据源个数判定,判定是否跨库事务
            if (connectionMap.size() >= maxTransactionDSCount) {
                throw new SQLException("事务下,不允许出现跨库数据源");
            }
        }
        connectionMap.put(dbIndex, connection);
        return connection;
    }

    private Connection getConnectionFromDs(String dbIndex) throws SQLException {
        if (null == username && null == password) {
            return dataSourceFacade.getDsMapHolder().get().get(dbIndex).getConnection();
        } else {
            return dataSourceFacade.getDsMapHolder().get().get(dbIndex).getConnection(username, password);
        }
    }

    @Override
    public void tryClose(String dbIndex) throws SQLException {

    }

    @Override
    public Connection getProxyConnection() {
        return this;
    }

    @Override
    public void removeCurrentStatement(Statement statement) {
        if (!openedStatements.remove(statement)) {
            log.warn("current statmenet ：" + statement + " doesn't exist!");
        }
    }

    @Override
    public boolean containDBIndex(String dbIndex) {
        return false;
    }

    @Override
    public Statement createStatement() throws SQLException {
        StatementFacade statement = new StatementFacade(dataSourceFacade, this);
        openedStatements.add(statement);
        return statement;
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        PreparedStatementFacade statement = new PreparedStatementFacade(dataSourceFacade, this, sql);
        openedStatements.add(statement);
        return statement;
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        this.isAutoCommit = autoCommit;
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return isAutoCommit;
    }

    @Override
    public void commit() throws SQLException {
        if (isAutoCommit) {
            return;
        }
        List<SQLException> sqlExceptionList = Lists.newLinkedList();
        for (Connection connection : connectionMap.values()) {
            //出现异常继续关闭
            try {
                connection.close();
            } catch (SQLException e) {
                log.error("关闭connection异常, e :", ExceptionUtils.getStackTrace(e));
                sqlExceptionList.add(e);
            }
        }
        ExceptionUtil.throwAllSQLExceptions(sqlExceptionList);
    }

    @Override
    public void rollback() throws SQLException {
        List<SQLException> sqlExceptionList = Lists.newLinkedList();
        for (Connection connection : connectionMap.values()) {
            //出现异常继续关闭
            try {
                connection.rollback();
            } catch (SQLException e) {
                log.error("关闭connection异常, e :", ExceptionUtils.getStackTrace(e));
                sqlExceptionList.add(e);
            }
        }
        ExceptionUtil.throwAllSQLExceptions(sqlExceptionList);
    }

    @Override
    public void close() throws SQLException {
        closed = true;
        for (Statement statement : openedStatements) {
            if (statement != null) {
                statement.close();
            }
        }
        for (Connection connection : connectionMap.values()) {
            if (null != connection) {
                connection.close();
            }
        }
    }

    @Override
    public boolean isClosed() throws SQLException {
        return closed;
    }
}
